with Ada.Strings; use Ada.Strings;
with Ada.Strings.Fixed;
with Ada.Strings.Unbounded;
with SDL.Keysym;
with Legend.Resources;
with Legend.Settings;
with Legend.StrRes;
with Legend.Traces;
with Legend.Colors;
package body Legend.Hero_State_Dialog is

   subtype Base_Type is Interactives.Interactive_Type;

   Dialog_Height         : constant := 240;
   Dialog_Width          : constant := 260;
   Dialog_Margin_Top     : constant := 0;
   Icon_Margin_Left      : constant := 10;
   Icon_Margin_Top       : constant := 10;
   Icon_Width            : constant := 60;
   Icon_Height           : constant := 60;
   Name_Margin_Top       : constant := 5;
   Name_Margin_Left      : constant := Icon_Width / 3;
   Name_Height           : constant := 20;
   Name_Left             : constant := Name_Margin_Left + Icon_Margin_Left;
   Name_Top              : constant := Name_Margin_Top + Icon_Height + Icon_Margin_Top;
   Left_Page_Margin_Top  : constant := 5;
   Left_Page_Margin_Left : constant := 10;
   Left_Page_Top         : constant := Name_Top + Name_Height + Left_Page_Margin_Top;
   Right_Page_Top        : constant := 10;
   Right_Page_Left       : constant := 5 + Dialog_Width / 2;
   Font_Size             : constant := 16.0;
   En_Size               : constant := Font_Size / 2.0;
   Line_Height           : constant := Font_Size * 1.2;

   Pro_Color : constant Color_Type := (R => 254, G => 146, B => 0, A => 255);
   Val_Color : constant Color_Type := (R => 240, G => 204, B => 0, A => 255);

   function Image (
      S        : String; 
      Width    : Natural; 
      Pad_Char : Character := ' '
      ) return String
   is
   begin
      if S'Length >= Width then
         return S(S'Last + 1 - Width .. S'Last);
      else
         return String'(1..Width - S'Length => Pad_Char) & S;
      end if;
   end Image;

   procedure Paint (this : in out Hero_State_Dialog_Type)
   is
      use Interfaces.C;
      R : SDL.Video.Rect_t := (
         X => Int16_t(Renders.Screen.W - Dialog_Width) / 2,
         Y => Dialog_Margin_Top,
         W => Dialog_Width,
         H => Dialog_Height);
      brush : Cairo.Context.Cairo_Context_Ref;
      Hero : Hero_Type renames All_World.Heroes(this.F_Hero);
      Head_Image : Picture_Type := Resources.Head_Images(Hero.Icon);

      procedure Paint_Name
      is
         tx, ty : double;
      begin
         tx := double(Name_Left);
         ty := Font_Size + double(Name_Top);
         brush.Move_To(tx, ty);
         brush.Show_Text(To_UTF8_Name(Hero.Name));
      end Paint_Name;

      procedure Paint_Left_Page1 
      is
         tx : double := double(Left_Page_Margin_Left);
         ty : double := double(Left_Page_Top);
         vx : double := tx + Font_Size * 3.0;
         MP_Color : Color_Type := (
            case Hero.MP_Type is
               when Yin => Colors.Purple,
               when Yan => Val_Color,
               when others => Colors.White);
         HP_Color : Color_Type := (
            if Hero.Hurt > 50 then 
               Colors.Red 
            elsif Hero.Weakness > 0 then 
               Colors.Green 
            else
               Val_Color);

         procedure Paint_Property (
            Name  : UTF8_String;
            Value : UTF8_String;
            Color : Color_Type := Val_Color
            )
         is
         begin
            ty := ty + Line_Height;
            brush.Move_To(tx, ty);
            this.Canvas.Set_Brush_Color(Pro_Color);
            brush.Show_Text(Name);
            brush.Move_To(vx, ty);
            this.Canvas.Set_Brush_Color(Color);
            brush.Show_Text(Value);
         end Paint_Property;
      begin
         tx := double(Left_Page_Margin_Left);
         ty := double(Left_Page_Top);

         Paint_Property(StrRes.Hero_State.Level, Hero.Level'Img);
         Paint_Property(StrRes.Hero_State.HP, Image(Traces.Image(Hero.HP), 7), HP_Color);
         Paint_Property(StrRes.Hero_State.MP, Image(Traces.Image(Hero.MP), 7), MP_Color);
         Paint_Property(StrRes.Hero_State.Stamina, Image(Hero.Stamina'Img & "/100", 7));
         Paint_Property(StrRes.Hero_State.Exp, Hero.Exp'Img);
         Paint_Property(StrRes.Hero_State.Levelup, Uint16_t'Image(Compute_Levelup_Exp(Hero)));

         brush.Stroke;
      end Paint_Left_Page1;

      procedure Paint_Left_Page2
      is
         tx, ty : double;
         procedure Paint_Item_Name (Item_Id : Uint16_t)
         is
         begin
            if Item_Id /= No_Item then
               brush.Move_To(tx, ty);
               declare
                  Item : Item_Type renames All_World.Items(Item_Id);
               begin
                  brush.Show_Text(To_UTF8_Name(Item.Name));
               end;
            end if;
         end Paint_Item_Name;

         procedure Paint_Book_Exp 
         is
            Book : Item_Type renames All_World.Items(Hero.Learning_Item);
            exp : Uint16_t := Compute_Book_Levelup_Exp(Hero, Book);
         begin
            brush.Move_To(tx, ty);
            this.Canvas.Set_Brush_Color(Val_Color);
            brush.Show_Text(Hero.Learning_Exp'Img & "/" & exp'Img);
         end Paint_Book_Exp;
      begin
         tx := double(Left_Page_Margin_Left);
         ty := double(Left_Page_Top);

         ty := ty + Line_Height;
         brush.Move_To(tx, ty);
         this.Canvas.Set_Brush_Color(Pro_Color);
         brush.Show_Text(StrRes.Hero_State.Equip);
         ty := ty + Line_Height;
         this.Canvas.Set_Brush_Color(Val_Color);
         Paint_Item_Name(Hero.Equips(e_Weapon));
         ty := ty + Line_Height;
         Paint_Item_Name(Hero.equips(e_Armor));

         ty := ty + Line_Height;
         brush.Move_To(tx, ty);
         this.Canvas.Set_Brush_Color(Pro_Color);
         brush.Show_Text(StrRes.Hero_State.Learning_Item);
         ty := ty + Line_Height;
         this.Canvas.Set_Brush_Color(Val_Color);
         Paint_Item_Name(Hero.Learning_Item);
         ty := ty + Line_Height;
         this.Canvas.Set_Brush_Color(Pro_Color);
         brush.Move_To(tx, ty);
         brush.Show_Text(StrRes.Hero_State.Require_Exp);
         ty := ty + Line_Height;
         if Hero.Learning_Item /= No_Item then
            Paint_Book_Exp;
         end if;

         brush.Stroke;
      end Paint_Left_Page2;

      procedure Paint_Right_Page1
      is
         tx, ty : double;
         procedure Paint_State (Name : UTF8_String; Value : Int16_t)
         is
            val : String := Fixed.Trim(Value'Img, Left);
            vx : double := tx + Font_Size * 5.0;
         begin
            ty := ty + Line_Height;
            brush.Move_To(tx, ty);
            this.Canvas.Set_Brush_Color(Val_Color);
            brush.Show_Text(Name);
            brush.Move_To(vx + En_Size * double(3 - val'Length), ty);
            this.Canvas.Set_Brush_Color(Colors.White);
            brush.Show_Text(val);
         end Paint_State;
      begin
         tx := double(Right_Page_Left);
         ty := double(Right_Page_Top);

         Paint_State(StrRes.Hero_State.Attack, Get_Skill(Hero, s_Attack));
         Paint_State(StrRes.Hero_State.Defense, Get_Skill(Hero, s_Defense));
         Paint_State(StrRes.Hero_State.Movement, Get_Skill(Hero, s_Movement));
         Paint_State(StrRes.Hero_State.Cure, Get_Skill(Hero, s_Cure));
         Paint_State(StrRes.Hero_State.Poison, Get_Skill(Hero, s_Poison));
         Paint_State(StrRes.Hero_State.Detoxify, Get_Skill(Hero, s_Detoxify));
         Paint_State(StrRes.Hero_State.Fist, Get_Skill(Hero, s_Fist));
         Paint_State(StrRes.Hero_State.Sword, Get_Skill(Hero, s_Sword));
         Paint_State(StrRes.Hero_State.Blade, Get_Skill(Hero, s_Blade));
         Paint_State(StrRes.Hero_State.Special, Get_Skill(Hero, s_Special));
         Paint_State(StrRes.Hero_State.Concealed, Get_Skill(Hero, s_Throw));

         brush.Stroke;
      end Paint_Right_Page1;

      procedure Paint_Right_Page2
      is
         tx, ty : double;
         k : Uint16_t;
      begin
         tx := double(Right_Page_Left);
         ty := double(Right_Page_Top) + Font_Size;
         this.Canvas.Set_Brush_Color(Pro_Color);
         brush.Move_To(tx, ty);
         brush.Show_Text(StrRes.Learned_Kongfus);
         ty := ty + Line_Height;
         for i in Kongfu_Index_Range loop
            k := Hero.Kongfu(i);
            if k not in 0 | No_Kongfu then
               brush.Move_To(tx, ty);
               this.Canvas.Set_Brush_Color(Val_Color);
               brush.Show_Text(To_UTF8_Name(All_World.Kongfus(k).Name));
               declare
                  level : Kongfu_Level_Range := Get_Kongfu_Level(Hero, i);
                  val : String := Fixed.Trim(level'Img, Left);
                  vx : double := tx + Font_Size * 5.0;
               begin
                  brush.Move_To(vx + En_Size * double(3 - val'Length), ty);
                  this.Canvas.Set_Brush_Color(Colors.White);
                  brush.Show_Text(val);
                  ty := ty + Line_Height;
               end;
            end if;
         end loop;
         brush.Stroke;
      end Paint_Right_Page2;
   begin
      this.Canvas.Bind(R, Save_Region => True);
      brush := this.Canvas.As_Brush;
      this.Canvas.Clear((A => 128, others => 0));
      this.Canvas.Set_Brush_Color((others => 255));
      this.Canvas.Draw_Border;
      this.Canvas.Set_True_Font(Unbounded.To_String(Settings.Dialog.Font.Name));
      brush.Set_Font_Size(font_size);
      Paint_Name;
      case this.F_Page_Index is
         when 1 =>
            Paint_Left_Page1;
            Paint_Right_Page1;
         when others =>
            Paint_Left_Page2;
            Paint_Right_Page2;
      end case;
      this.Canvas.Accept_Brush_Changed;
      Head_Image.Position := (0, 0);
      this.Canvas.Draw_Picture(Head_Image, (Icon_Margin_Left, Icon_Margin_Top));
      this.Canvas.Accept_SDL_Changed;
   end Paint;

   procedure On_Timer (
      this          : in out Hero_State_Dialog_Type;
      Timer         : Hash_Type;
      Count         : Natural;
      Cancel_Bubble : in out Boolean
      )
   is
   begin
      Base_Type(this).On_Timer(Timer, Count, Cancel_Bubble);
      Cancel_Bubble := True;
   end On_Timer;

   procedure On_Key_Up (
      this : in out Hero_State_Dialog_Type;
      Message : SDL.Events.Keyboard_Event_t;
      Cancel_Bubble : in out Boolean
      )
   is
      Team : Uint16_Array renames All_World.Leader.Team;
   begin
      case Message.Keysym.Sym is
         when SDL.Keysym.K_Escape  =>
            this.Canvas.Restore_Overlap_Surface;
            this.Close;
         when SDL.Keysym.K_Up =>
            declare
               ti : Natural renames this.F_Team_Index;
               h : Uint16_t := this.F_Hero;
            begin
               loop
                  if ti > Team'First then
                     ti := ti - 1;
                  else
                     ti := Team'Last;
                  end if;
                  h := Team(ti);
                  exit when h /= No_Hero;
               end loop;
               if h /= this.F_Hero then
                  this.F_Hero := h;
                  this.Canvas.Invalidate;
               end if;
            end;
         when SDL.Keysym.K_Down =>
            declare
               ti : Natural renames this.F_Team_Index;
               h : Uint16_t := this.F_Hero;
            begin
               loop
                  if ti < Team'Last then
                     ti := ti + 1;
                  else 
                     ti := Team'First;
                  end if;
                  h := All_World.Leader.Team(ti);
                  exit when h /= No_Hero;
               end loop;
               if h /= this.F_Hero then
                  this.F_Hero := h;
                  this.Canvas.Invalidate;
               end if;
            end;
         when SDL.Keysym.K_Left | SDL.Keysym.K_Right =>
            this.F_Page_Index := 3 - this.F_Page_Index;
            this.Canvas.Invalidate;
         when others =>
            null;
      end case;
   end On_Key_Up;

   procedure Initialize (this : in out Hero_State_Dialog_Type)
   is
   begin
      Base_Type(this).Initialize;
      this.F_Team_Index := 1;
      this.F_Hero := 0;
      this.F_Page_Index := 1;
   end Initialize;

   procedure Paint (this : in out Hero_State_Box_Type)
   is
      use Interfaces.C;
      who : Hero_Type renames All_World.Heroes(this.Hero);
      S : SDL.Video.Surface_Access_t := Renders.Screen;
      R : SDL.Video.Rect_t := (
         W => Uint16_t(this.Size.x), 
         H => Uint16_t(this.Size.y), 
         others => 0);
      p : Picture_Type := Resources.Head_Images(who.Icon);
   begin
      if this.Canvas.As_Surface in null then
         this.Canvas.Bind(Region => R);
      end if;
      this.Canvas.Clear(this.Back_Color);
      this.Canvas.Draw_Round_Rect(
         Region     => (0, 0, R.W, R.H),
         Back_Color => (B => 255, others => 128),
         Fore_Color => Colors.Black,
         Width      => 2.0,
         Groove     => True);
      declare
         B : Cairo.Context.Cairo_Context_Ref := this.Canvas.As_Brush;
         tx : double := double(R.W) - 110.0;
         ty : double := 30.0;

         procedure Paint_Property (Key, Value : UTF8_String)
         is
         begin
            ty := ty + Line_Height;
            B.Move_To(tx, ty);
            B.Show_Text(Key & ":" & Value);
         end Paint_Property;
      begin
         this.Canvas.Set_Brush_Color(Val_Color);
         this.Canvas.Set_True_Font(Unbounded.To_String(Settings.Dialog.Font.Name));
         B.Set_Font_Size(Font_Size);
         B.Move_To(tx, ty);
         B.Show_Text(To_UTF8_Name(who.Name));
         Paint_Property(StrRes.Hero_State.Stamina, who.Stamina'Img);
         Paint_Property(StrRes.Hero_State.HP, Traces.Image(who.HP));
         Paint_Property(StrRes.Hero_State.MP, Traces.Image(who.MP));
         B.Stroke;
         this.Canvas.Accept_Brush_Changed;
      end;
      P.Position := (0, 0);
      this.Canvas.Draw_Picture(P, (10, 10));
      this.Canvas.Accept_SDL_Changed;
   end Paint;

   procedure Prepare (this : in out Hero_State_Box_Type) 
   is
      p : Point_Type renames this.Position;
   begin
      p := this.Margin;
      if p.x < 0 then
         p.x := p.x + Int16_t(Renders.Screen.W) - this.Size.x;
      end if;
      if p.y < 0 then
         p.y := p.y + Int16_t(Renders.Screen.H) - this.Size.y;
      end if;
   end Prepare;

   procedure Initialize (this : in out Hero_State_Box_Type)
   is
   begin
      this.Hero := No_Hero;
      this.Size := (180, 100);
      this.Margin := (-30, 50);
   end Initialize;
end Legend.Hero_State_Dialog;
